﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Windows.Forms;
using CSScriptLibrary;
using Microsoft.CSharp;
using Testpad1;
using csscript;

namespace Testpad1
{
    public interface ISum
    {
        int Sum(int a, int b);
    }
}

namespace Testpad
{
    public class Program
    {
        private static void LoadAssemblyForreflection()
        {
            CSScriptLibrary.AssemblyResolver.IsNamespaceDefinedInAssembly(@"F:\cs-script\engine\Testpad\test.dll", "scripting");
        }

        private static void CompileWithDoc()
        {
            string asmFile = CSScript.CompileWithConfig(
                                    @"E:\My Dropbox\Public\Marco Singer\test.cs",
                                    null,
                                    false,
                                    CSScript.GlobalSettings,
                                    @"/doc:""E:\My Dropbox\Public\Marco Singer\test.xml""");

            var asm = Assembly.LoadFrom(asmFile);
        }

        private static void CompilerSwallowsErrors()
        {
            CSharpCodeProvider codeProvider = new CSharpCodeProvider();

            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateExecutable = false;
            parameters.OutputAssembly = Path.GetTempFileName();
            //parameters.CompilerOptions = "/warnaserror";
            CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters,
                                                      @"class Test
                                                        {
                                                            int TestMethod()
                                                            {
                                                                return 5;
                                                                return 10;
                                                            }
                                                        }");
            foreach (var e in results.Errors)
                Trace.WriteLine(e.ToString());
        }

        private static void ExecuteInSandbox(PermissionSet permissionSet, Action action)
        {
            permissionSet.PermitOnly();
            try
            {
                action();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetType().ToString() + ": " + e.Message);
            }
            finally
            {
                CodeAccessPermission.RevertPermitOnly();
            }
        }

        private static void ResourcesTest()
        {
            var script = CSScript.Load(@"P:\My Dropbox\Public\Leo Meyer\ScriptA.cs");
        }

        private static void ClasslessCahe()
        {
            var test = CSScript.LoadMethod(
                        @"public static int Test(int a, int b)
                          {
                              return a + b;
                          }");
            //.GetStaticMethod();

            var test1 = CSScript.LoadMethod(
                        @"public static int Test(int a, int b)
                          {
                              return a + b ;
                          }");
            //.GetStaticMethod();

            //var t = test(1,2);
            //var t1 = test(1, 2);
        }

        private static void Clr4()
        {
            var Test = CSScript.LoadMethod(
                        @"public static void Test()
                          {
                             dynamic d = ""123"";
                             Console.WriteLine(d.ToString() + "": "" +d.Length);
                          }")
                         .GetStaticMethod();
            Test();
        }

        private static void DynamicMethodMemoryConsumptionTest()
        {
            Assembly assembly = CSScript.LoadCode(
                           @"using System;
                              public class Claculator
                              {
                                  public void Sum(int a, int b)
                                  {
                                      Console.WriteLine(a + b);
                                  }

                                  public void Multiply(int a, int b)
                                  {
                                      Console.WriteLine(a * b);
                                  }
                              }");

            AsmHelper script = new AsmHelper(assembly);

            MethodInfo methodInfo = FindMethod(assembly, "Claculator.Sum", typeof(int), typeof(int));

            MessageBox.Show("AboutToStart");
            for (int i1 = 0; i1 < 1000; i1++)
            {
                for (int i = 0; i < 1000000; i++)
                {
                    object dynamicMethod = new FastInvoker(methodInfo).GetMethodInvoker();
                }

                if (MessageBox.Show("Continue?", "", MessageBoxButtons.YesNo) == DialogResult.No)
                    return;

                GC.Collect();
            }
            //var ttt = dynamicMethod.GetType().Assembly;

            //FastInvokeDelegate Sum = script.GetMethodInvoker("Claculator.Sum", 0, 0);
        }

        private static MethodInfo FindMethod(Assembly asm, string methodName, params Type[] args)
        {
            string[] names = methodName.Split(".".ToCharArray());
            if (names.Length < 2)
                throw new ApplicationException("Invalid method name format (must be: \"<type>.<method>\")");

            string methodShortName = names[names.Length - 1];
            string typeName = names[names.Length - 2];
            MethodInfo method;

            foreach (Module m in asm.GetModules())
            {
                Type[] types;
                if (names[0] == "*")
                    types = m.GetTypes();
                else
                    types = m.FindTypes(Module.FilterTypeName, names[names.Length - 2]);

                foreach (Type t in types)
                    if (methodShortName == "*")
                    {
                        MethodInfo[] methods = t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                        if (methods.Length != 0)
                            return methods[0]; //return the very first method
                    }
                    else if ((method = t.GetMethod(methodShortName, args)) != null)
                    {
                        if (typeName.EndsWith("*"))
                            return method;
                        else if (methodName == method.DeclaringType.FullName + "." + methodShortName)
                            return method;
                    }
            }

            string msg = "Method " + methodName + "(";
            if (args.Length > 0)
            {
                foreach (Type arg in args)
                    msg += arg.ToString() + ", ";
                msg = msg.Remove(msg.Length - 2, 2);
            }
            msg += ") cannot be found.";

            throw new ApplicationException(msg);
        }

        static object Create()
        {
            return new
            {
                Name = "trtytr",
                Timestamp = Environment.TickCount
            };
        }



        public class TestCustomer
        {
            public string Name { get; set; }
        }

        public interface IScript
        {
            void Hello(string greeting);
        }

        static void TestWithAsmHelperAndUnloadingAssembly()
        {

            //            var Sum = new AsmHelper(CSScript.LoadMethod(
            //           @"public static int Sum(int a, int b)
            //              {
            //                  System.Diagnostics.Debugger.Break();
            //                  return a + b;
            //              }", null, true))
            //             .GetStaticMethod();

            //            int result = (int)Sum(1, 2);

            //we cannot use HelloScript.cs as class Script must 
            //be serializable or derived from MarshalByRefObject
            var code = @"using System;
 
                     public class Script : MarshalByRefObject
                     {
                         public void Hello(string greeting)
 	                     {
                            System.Diagnostics.Debug.Assert(false);
 	                         Console.WriteLine(greeting);
 	                     }
                     }";

            //Note usage of helper.CreateAndAlignToInterface<IScript>("Script") is also acceptable
            using (var helper = new AsmHelper(CSScript.CompileCode(code, null, true), null, true))
            {
                IScript script = helper.CreateAndAlignToInterface<IScript>("*");

                script.Hello("Hi there...");
            }
        }

        private static void Main(string[] args)
        {

            var PrintSum = CSScript.LoadMethod(
                        @"public static void PrintSum(int a, int b)
                                  {
                                      Console.WriteLine((a+b));
                                  }")
                          .GetStaticMethod();
            PrintSum(1, 2);
            TestWithAsmHelperAndUnloadingAssembly();
            {
                Environment.CurrentDirectory = @"E:\cs-script\Dev\Support\Tomas Blazo\cs_script\cs_script\bin\Debug";
                var parser = new CSharpParser(@"E:\cs-script\Dev\Support\Tomas Blazo\cs_script\cs_script\bin\Debug\test.cs", true);
                foreach (CSharpParser.CmdScriptInfo cmdScript in parser.CmdScripts)
                {
                    if (cmdScript.preScript)
                    {
                        CSScript.Rethrow = true;
                        CSScript.Execute(null, cmdScript.args);
                    }
                }

                var script3 = CSScript.Load(@"E:\cs-script\Dev\Support\Tomas Blazo\cs_script\cs_script\bin\Debug\test.cs")
                    .CreateInstance("Script")
                    .AlignToInterface<IScript>();
            }
            {
                var executor = new AsmHelper(CSScript.Compile(@"E:\cs-script\Dev\Support\Simon_2\Script_1.cs"), null, false);
                var script1 = executor.CreateAndAlignToInterface<IScript>("*");
                executor.Dispose();
                executor = new AsmHelper(CSScript.Compile(@"E:\cs-script\Dev\Support\Simon_2\Script_1.cs"), null, false);
                script1 = executor.CreateAndAlignToInterface<IScript>("*");
            }

            SumLocal(1, 2);
            Job.Test(); return;

            var tt = SumRemote(1, 3); return;
            //var ttt = ScriptTest.Main(null);
            //Type tp= Type.GetType("<>f__AnonymousType0`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]");

            // <>f__AnonymousType0<string, int> _obj;
            ObjectCasterTests.Test(); return;
            EvalTests.Test(); return;

            CSScript.CacheEnabled = false;

            //CSScript.
            Assembly scriptAssembly = CSScript.Load(@"E:\cs-script\Main\TestScripts\classless_2_2.cs");
            //            Assembly scriptAssembly = CSScript.LoadCode(@"using System;
            //                                                          using System.Reflection;
            //                                                          using System.Linq;
            //                                                          using CSScriptLibrary;
            //                                                          //css_ref System.Windows.Forms.dll;
            //                                                          //css_reference Microsoft.CSharp;

            //                                                          class Script
            //                                                          {
            //                                                              static public void Main()
            //                                                              {
            //                                                                    //Console.WriteLine(""Script file (EnvVar):     "" + Environment.GetEnvironmentVariable(""EntryScript""));

            //                                                                    CSScript.IsScriptAssembly(Assembly.GetExecutingAssembly());                              
            //                                                                    Console.WriteLine(""Script file (reflection): "" + CSScript.GetScriptAssemblyScriptName(Assembly.GetExecutingAssembly()));
            //                                                                    Console.WriteLine(""Script file (reflection): "" + Assembly.GetExecutingAssembly().GetScriptAssemblyScriptName());

            //                                                                    dynamic scriptAttr = (from a in Assembly.GetExecutingAssembly().GetCustomAttributes(true)
            //                                                                                          where a.GetType().Name == ""ScriptedCodeAttribute""
            //                                                                                          select a).FirstOrDefault();
            //                                                                    if (scriptAttr != null)
            //                                                                        Console.WriteLine(""Script file (reflection): "" +scriptAttr.ScriptFile);

            //                                                                    System.Windows.Forms.MessageBox.Show(""Just a test!"");
            //                                                              }
            //                                                          }");

            AsmHelper script = new AsmHelper(scriptAssembly);
            script.Invoke("*.Main");
            return;

            //Assembly scriptAssembly = Assembly.Load(File.ReadAllBytes(scriptAssemblyFile));
            //File.Delete(scriptAssemblyFile);

            //Assembly scriptAssembly = CSScript.Load(@"E:\Support\Michael Endres\Script.cs", null, true);



            Host.Test.DisableCache(); return;
            //DynamicMethodMemoryConsumptionTest(); return;

            CSScript.GlobalSettings.InMemoryAsssembly = true;

            var v = CSScript.LoadMethod(
                       @"using System.IO;
                         using GeneralMsg = System.Collections.Generic.Dictionary<string, object>;
                         using CSS=CSScriptLibrary;

                          public static void Test()
                          {
                              using (var f = File.Open(""somefile.txt"", FileMode.OpenOrCreate))
                                  Console.WriteLine(""File.Open: success"");
                          }", "test.dll", false);
            return;
            Clr4();
            //Concurrency.Test();
            //ResourcesTest();
            //ClasslessCahe();
            return;
            var CreateSomeFile = CSScript.LoadMethod(
                        @"using System.IO;

                          public static void Test()
                          {
                              using (var f = File.Open(""somefile.txt"", FileMode.OpenOrCreate))
                                  Console.WriteLine(""File.Open: success"");
                          }")
                         .GetStaticMethod();

            var permissionSet = new PermissionSet(PermissionState.None);
            permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

            CreateSomeFile();

            //Sandbox.Execute(permissionSet, () =>
            //{
            //    CreateSomeFile();
            //});

            Sandbox.With(SecurityPermissionFlag.AllFlags)
                   .Execute(() =>
                            {
                                try
                                {
                                    CreateSomeFile();
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(e.Message);
                                }
                            });

            CreateSomeFile();

            //CompileWithDoc(); return;

            //             string code1 =
            //                               @"static public int Sum(int a, int b)
            //                                 {
            //                                     #warning test warning
            //                                     return a + b;
            //                                 }";
            //try
            //{
            //    string script = Path.GetTempFileName();
            //    File.WriteAllText(script, code1);

            //    string assembly = CSScript.CompileWithConfig(script, null, false, CSScript.GlobalSettings, "/warnaserror");
            //}
            //catch (Exception e)
            //{
            //    Console.WriteLine(e.Message);
            //}
            //compiler detailed error

            {
                try
                {
                    CSScript.LoadCode("invalid code");
                }
                catch (Exception e)
                {
                    CompilerErrorCollection errors = (CompilerErrorCollection)e.Data["Errors"];
                    foreach (CompilerError err in errors)
                    {
                        Console.WriteLine("{0}({1},{2}): {3} {4}: {5}",
                            err.FileName, err.Line, err.Column, err.IsWarning ? "warning" : "error", err.ErrorNumber, err.ErrorText);
                    }
                }
            }
            {
                string code =
                    //@"//css_co /warnaserror+;
                                @"static public int Sum(int a, int b)
                                  {
                                      #warning test warning
                                      return a + b;

                                      return 7;
                                  }";
                try
                {
                    CSScript.LoadMethod(code);

                    FileInfo scriptInfo = CSScript.CompilingHistory.Last().Key;
                    CompilerErrorCollection errors = CSScript.CompilingHistory.Last().Value.Errors;

                    StringBuilder compileErr = new StringBuilder();
                    foreach (CompilerError err in errors)
                    {
                        Console.Write(err.FileName);
                        Console.Write("(");
                        Console.Write(err.Line);
                        Console.Write(",");
                        Console.Write(err.Column);
                        Console.Write("): ");
                        if (err.IsWarning)
                            Console.Write("warning ");
                        else
                            Console.Write("error ");
                        Console.Write(err.ErrorNumber);
                        Console.Write(": ");
                        Console.Write(err.ErrorText);
                        Console.WriteLine();
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.WriteLine(e.Message);
                    CompilerErrorCollection errors = (CompilerErrorCollection)e.Data["Errors"];

                    StringBuilder compileErr = new StringBuilder();
                    foreach (CompilerError err in errors)
                    {
                        Console.Write(err.FileName);
                        Console.Write("(");
                        Console.Write(err.Line);
                        Console.Write(",");
                        Console.Write(err.Column);
                        Console.Write("): ");
                        if (err.IsWarning)
                            Console.Write("warning ");
                        else
                            Console.Write("error ");
                        Console.Write(err.ErrorNumber);
                        Console.Write(": ");
                        Console.Write(err.ErrorText);
                        Console.WriteLine();
                    }
                }
            }
            return;
            string scriptCode = @"//css_co /define:TESTING;
                                  static public int Sum(int a, int b)
                                  {
                                    #if TESTING
                                      return 7;
                                    #else
                                      return a + b;
                                    #endif
                                  }";

            //var Sum = CSScript.LoadCode(scriptCode)
            //                  .GetStaticMethodWithArgs("*.Sum", typeof(int), typeof(int));

            var dir = csscript.CSSEnvironment.GetCacheDirectory(Path.Combine(Path.GetTempPath(), "CSSCRIPT\\dummy.cs"));
            CSScript.CacheEnabled = false;

            var Sum = CSScript.LoadMethod(scriptCode)
                              .GetStaticMethodWithArgs("*.Sum", typeof(int), typeof(int));

            var result7 = Sum(1, 4);

            SumLocal(1, 2);
            var t1 = CSScriptLibrary.AssemblyResolver.FindLocalAssembly("test.xml", @"F:\cs-script\engine\Testpad");

            //var aa = Assembly.ReflectionOnlyLoadFrom(@"F:\cs-script\engine\Testpad\test.dll");

            //LoadAssemblyForreflection();
            //GC.Collect();
            //MessageBox.Show("test");
            return;
            var t = CSScriptLibrary.AssemblyResolver.FindLocalAssembly("System.Xml", "c:\\windows\\microsoft.net\\framework\\v2.0.50727");

            var result = SumLocal(1, 2);
            var result1 = SumLocal(1, 3);
            var result2 = SumRemote(1, 2);
        }

        private static int SumRemote(int a, int b)
        {

            //this will also unload and delete the compiled assembly file after the execution
            string scriptCode = @"using System;
                                  public class Calc : MarshalByRefObject
                                  {
                                     public int Sum(int a, int b)
                                     {
                                        return a + b;
                                     }

                                     public static int Sum1(int a, int b)
                                     {
                                        return a + b;
                                     }
                                  }";

            string asmFile = CSScript.CompileCode(scriptCode, null, false);
            using (AsmHelper helper = new AsmHelper(asmFile, null, true))
            {
                //The following code will always fail as obj is a Remoting TransparentProxy and
                //it does not have members of ISum.
                //object obj = helper.CreateObject("Calc");
                //ISum calc = obj.AlignToInterface<ISum>();

                //The following code will always secceed as the aligning will be conducted in the remote
                //AppDomain where the object was created
                var ttt = helper.Invoke("*.Sum1", 1, 3);
                ISum calc = helper.CreateAndAlignToInterface<ISum>("Calc");

                return calc.Sum(a, b);
            }
        }

        private static int SumLocal(int a, int b)
        {
            string scriptCode = @"//css_args /co:/define TTT;
                                  using System;
                                  public class Calc
                                  {
                                     public int Sum(int a, int b)
                                     {
                                        return a + b;
                                     }
                                  }";

            ISum calc = CSScript.LoadCode(scriptCode, null, false)
                                .CreateInstance("Calc")
                                .AlignToInterface<ISum>();

            return calc.Sum(a, b);
        }
    }
}

//public class ILReader  : IEnumerable<ILInstruction>
//{
//   Byte[] m_byteArray;
//   Int32 m_position;
//   MethodBase m_enclosingMethod;

//   static OpCode[] s_OneByteOpCodes = new OpCode[0x100];
//   static OpCode[] s_TwoByteOpCodes = new OpCode[0x100];
//   static ILReader()
//   {
//     foreach (FieldInfo fi in typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static))
//     {
//       OpCode opCode = (OpCode)fi.GetValue(null);
//       UInt16 value = (UInt16)opCode.Value;
//       if (value < 0x100)
//         s_OneByteOpCodes[value] = opCode;
//       else if ((value & 0xff00) == 0xfe00)
//         s_TwoByteOpCodes[value & 0xff] = opCode;
//     }
//   }

//   public ILReader(MethodBase enclosingMethod)
//   {
//     this.m_enclosingMethod = enclosingMethod;
//     MethodBody methodBody = m_enclosingMethod.GetMethodBody();
//     this.m_byteArray = (methodBody == null) ? new Byte[0] : methodBody.GetILAsByteArray();
//     this.m_position = 0;
//   }

//   public IEnumerator<ILInstruction>GetEnumerator()
//   {
//     while (m_position < m_byteArray.Length)
//       yield return Next();

//     m_position = 0;
//     yield break;
//   }
//   System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()  { return this.GetEnumerator(); }

//   ILInstruction Next()
//   {
//     Int32 offset = m_position;
//     OpCode opCode = OpCodes.Nop;
//     Int32 token = 0;

//     // read first 1 or 2 bytes as opCode
//     Byte code = ReadByte();
//     if (code != 0xFE)
//       opCode = s_OneByteOpCodes[code];
//     else
//     {
//       code = ReadByte();
//       opCode = s_TwoByteOpCodes[code];
//     }

//     switch (opCode.OperandType)
//     {
//       case OperandType.InlineNone:
//         return new InlineNoneInstruction(m_enclosingMethod, offset, opCode);

//       case OperandType.ShortInlineBrTarget:
//         SByte shortDelta = ReadSByte();
//         return new ShortInlineBrTargetInstruction(m_enclosingMethod, offset, opCode, shortDelta);

//       case OperandType.InlineBrTarget: Int32 delta = ReadInt32();     return new ...;
//       case OperandType.ShortInlineI:   Byte int8 = ReadByte();        return new ...;
//       case OperandType.InlineI:        Int32 int32 = ReadInt32();     return new ...;
//       case OperandType.InlineI8:       Int64 int64 = ReadInt64();     return new ...;
//       case OperandType.ShortInlineR:   Single float32 = ReadSingle(); return new ...;
//       case OperandType.InlineR:        Double float64 = ReadDouble(); return new ...;
//       case OperandType.ShortInlineVar: Byte index8 = ReadByte();      return new ...;
//       case OperandType.InlineVar:      UInt16 index16 = ReadUInt16(); return new ...;
//       case OperandType.InlineString:   token = ReadInt32(); return new ...;
//       case OperandType.InlineSig:      token = ReadInt32(); return new ...;
//       case OperandType.InlineField:    token = ReadInt32(); return new ...;
//       case OperandType.InlineType:     token = ReadInt32(); return new ...;
//       case OperandType.InlineTok:      token = ReadInt32(); return new ...;

//       case OperandType.InlineMethod:
//         token = ReadInt32();
//         return new InlineMethodInstruction(m_enclosingMethod, offset, opCode, token);

//       case OperandType.InlineSwitch:
//         Int32 cases = ReadInt32();
//         Int32[] deltas = new Int32[cases];
//         for (Int32 i = 0; i < cases; i++) deltas[i] = ReadInt32();
//         return new InlineSwitchInstruction(m_enclosingMethod, offset, opCode, deltas);

//       default:
//         throw new BadImageFormatException("unexpected OperandType " + opCode.OperandType);
//     }
//   }

//   Byte ReadByte() { return (Byte)m_byteArray[m_position++]; }
//   SByte ReadSByte() { return (SByte)ReadByte(); }

//   UInt16 ReadUInt16() { m_position += 2; return BitConverter.ToUInt16(m_byteArray, m_position - 2); }
//   UInt32 ReadUInt32() { m_position += 4; return BitConverter.ToUInt32(m_byteArray, m_position - 4); }
//   UInt64 ReadUInt64() { m_position += 8; return BitConverter.ToUInt64(m_byteArray, m_position - 8); }

//   Int32 ReadInt32() { m_position += 4; return BitConverter.ToInt32(m_byteArray, m_position - 4); }
//   Int64 ReadInt64() { m_position += 8; return BitConverter.ToInt64(m_byteArray, m_position - 8); }

//   Single ReadSingle() { m_position += 4; return BitConverter.ToSingle(m_byteArray, m_position - 4); }
//   Double ReadDouble() { m_position += 8; return BitConverter.ToDouble(m_byteArray, m_position - 8); }
//}

namespace Host
{
    public interface IMultiplier
    {
        TestCollection GetCollection();
    }

    public class TestCollection : List<string>
    {
    }

    public class Test
    {
        public static void DisableCache()
        {
            CSScript.CacheEnabled = false;

            string scriptAssemblyFile = CSScript.Compile(@"E:\Support\Michael Endres\Script.cs", null, true);
            Assembly scriptAssembly = CSScript.Load(@"E:\Support\Michael Endres\Script.cs", null, true);

            IMultiplier multiplier = (IMultiplier)scriptAssembly.CreateObject("Scripts.Script");

            var test = multiplier.GetCollection();

            string test2 = String.Empty;

            foreach (string temp in test)
            {
                Debug.WriteLine(temp);
            }
        }
    }
}